﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using Windows.UI.Core;
using Windows.UI.Xaml;

namespace AllanXingCommonLibrary
{
    public static class AwiteHelper
    {
        //public static TaskAwaiter GetAwaiter(this TimeSpan timeSpan)
        //{

        //    return TaskEx.Delay(timeSpan).GetAwaiter();
        //}
        public static TaskAwaiter GetAwaiter(this int ms)
        {
            return Task.Delay(ms).GetAwaiter();
        }

        public static TaskAwaiter GetAwaiter(this string s)
        {
            return new Task(() => { }).GetAwaiter();
        }

        public static AllanControlAwaiter GetAwaiter(this DependencyObject control)
        {
            return new AllanControlAwaiter(control);
        }

        public static Task<TResult> SelectMany<TSource, TCollection, TResult>(this Task<TSource> source,
            Func<TSource, Task<TCollection>> collectionSelector,
            Func<TSource, TCollection, TResult> resultSelector)
        {
            if (source == null) throw new ArgumentNullException("source");
            if (collectionSelector == null) throw new ArgumentNullException("collectionSelector");
            if (resultSelector == null) throw new ArgumentNullException("resultSelector");

            return source.ContinueWith(t =>
            {
                return collectionSelector(t.Result).
                    ContinueWith(c => resultSelector(t.Result, c.Result), TaskContinuationOptions.NotOnCanceled);
            }, TaskContinuationOptions.NotOnCanceled).Unwrap();
        }

        /// <summary>
        /// var aResult = DoAAsync("");
        /// var bResult = aResult.Then(s => DoBAsync(s));
        /// var cResult = bResult.Then(s => DoCAsync(s));
        /// </summary>
        /// <typeparam name="T1"></typeparam>
        /// <typeparam name="T2"></typeparam>
        /// <param name="first"></param>
        /// <param name="next"></param>
        /// <returns></returns>
        public static Task<T2> Then<T1, T2>(this Task<T1> first, Func<T1, Task<T2>> next)
        {
            if (first == null) throw new ArgumentNullException("first");
            if (next == null) throw new ArgumentNullException("next");

            var tcs = new TaskCompletionSource<T2>();
            first.ContinueWith(delegate
            {
                if (first.IsFaulted) tcs.TrySetException(first.Exception.InnerExceptions);
                else if (first.IsCanceled) tcs.TrySetCanceled();
                else
                {
                    try
                    {
                        var t = next(first.Result);
                        if (t == null) tcs.TrySetCanceled();
                        else t.ContinueWith(delegate
                        {
                            if (t.IsFaulted) tcs.TrySetException(t.Exception.InnerExceptions);
                            else if (t.IsCanceled) tcs.TrySetCanceled();
                            else tcs.TrySetResult(t.Result);
                        }, TaskContinuationOptions.ExecuteSynchronously);
                    }
                    catch (Exception exc) { tcs.TrySetException(exc); }
                }
            }, TaskContinuationOptions.ExecuteSynchronously);
            return tcs.Task;
        }

        public static Task Then(this Task first, Func<Task> next)
        {
            if (first == null) throw new ArgumentNullException("first");
            if (next == null) throw new ArgumentNullException("next");

            var tcs = new TaskCompletionSource<object>();
            first.ContinueWith(delegate
            {
                if (first.IsFaulted) tcs.TrySetException(first.Exception.InnerExceptions);
                else if (first.IsCanceled) tcs.TrySetCanceled();
                else
                {
                    try
                    {
                        var t = next();
                        if (t == null) tcs.TrySetCanceled();
                        else t.ContinueWith(delegate
                        {
                            if (t.IsFaulted) tcs.TrySetException(t.Exception.InnerExceptions);
                            else if (t.IsCanceled) tcs.TrySetCanceled();
                            else tcs.TrySetResult(null);
                        }, TaskContinuationOptions.ExecuteSynchronously);
                    }
                    catch (Exception exc) { tcs.TrySetException(exc); }
                }
            }, TaskContinuationOptions.ExecuteSynchronously);
            return tcs.Task;
        }
    }

    public class AllanControlAwaiter : INotifyCompletion
    {
        private readonly DependencyObject m_control;
        private bool _IsCompleted = false;
        public AllanControlAwaiter(DependencyObject control)
        {
            m_control = control;
        }

        public bool IsCompleted
        {
            get { return _IsCompleted; }
        }

        public void OnCompleted(Action continuation)
        {
            var result = m_control.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => continuation());
            result.AsTask().Wait();
            _IsCompleted = true;
        }

        public void GetResult() { }


    }
}
